﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using UCL.Powersource;

namespace PowersourceUIToolkit
{

    /// <summary>
    /// Cache supports more views per viewmodel - by providing different Context
    /// However current public API almost exclusively support only 1-1 V-VM scenario
    /// </summary>
    internal class ViewCache : IDisposable
    {
        ILog Log = LogManager.GetLog(typeof(ViewCache));
        List<ViewCacheItem> vcCollection = new List<ViewCacheItem>();
        Dictionary<IViewModel, ViewCacheItem> DefaultVCICollection = new Dictionary<IViewModel, ViewCacheItem>();

        public ViewCacheItem AddToCache(IViewModel vm, FrameworkElement view, object context = null)
        {
            if (view == null || vm == null) throw new ArgumentNullException("View and ViewModel cannot be null when adding to cache.");
            var allVIC = vcCollection.Where(i => i.ViewModel == vm && i.Context == context);
            if (allVIC.Count() > 1) throw new InvalidOperationException("Cache can contains only 1 View per pair (viewmodel,Context)");

            if (allVIC.Count() == 0)
            {
                var vit = new ViewCacheItem(vm, view, context);
                vcCollection.Add(vit);
                if (!DefaultVCICollection.ContainsKey(vm))
                {
                    DefaultVCICollection.Add(vm, vit);
                }
                return vit;
            }
            else
            {
                var t = allVIC.Single();
                if (t.View != view)
                    throw new InvalidOperationException(String.Format("Cache can contains only 1 View per pair (viewmodel,Context). For VM {0} and Context {1} there is already a View {2}", vm, view, context));
                else
                    Log.Trace("Call AddToCache ignored. View  {0} is already associated with the Viewmodel {1}", view, vm);
                return t;
            }
        }

        public ViewCacheItem EnsureInCache(IViewModel vm, FrameworkElement view, object context = null)
        {

            var itemForContext = vcCollection.SingleOrDefault(a => vm == a.ViewModel && a.Context == context);
            if (itemForContext == null)
            {
                return AddToCache(vm, view, context);
            }
            return itemForContext;
        }

        public FrameworkElement GetDefaultView(IViewModel vm)
        {
            var itemForContext = vcCollection.SingleOrDefault(a => vm == a.ViewModel && a.Context == null);

            if (itemForContext != null)
            {
                if (!DefaultVCICollection.ContainsKey(vm)) throw new InvalidOperationException(string.Format("ViewCache does not contains a default View for View model {0}. However itForFE contains a View {1} with Context {2} for that VM. ", vm, itemForContext.View, itemForContext.Context));
                return DefaultVCICollection[vm].View;
            }
            else return null;
        }

        public ViewCacheItem GetCacheItemForView(FrameworkElement el)
        {
            var allVCI = vcCollection.Where(a => a.View == el);
            if (allVCI.Count() > 1) throw new InvalidOperationException(string.Format("ViewCache contains {0} items for element {1}[{2}]", allVCI.Count(), el.GetType(), el.Name));
            return allVCI.FirstOrDefault();
        }

        public ViewCacheItem GetDefaultCacheItemForViewModel(IViewModel im)
        {
            ViewCacheItem vci;
            if (DefaultVCICollection.TryGetValue(im, out vci))
            {
                return vci;
            }
            return null;
        }

        public void Dispose()
        {
            foreach (var vci in this.DefaultVCICollection)
            {
                vci.Value.Dispose();
            }
            DefaultVCICollection.Clear();
        }
    }

    public static class ViewLocator
    {
        private static IUIManager _UIManager;
        public static IUIManager UIManager
        {
            get { return _UIManager ?? (_UIManager = IoC.Get<IUIManager>()); }
        }

        private readonly static ViewCache DefaultViewCache = new ViewCache();

        public static ViewCacheItem GetViewCacheItem(FrameworkElement el)
        {
            return DefaultViewCache.GetCacheItemForView(el);
        }

        public static ViewCacheItem GetDefaultCacheItemForViewModel(IViewModel vm)
        {
            return DefaultViewCache.GetDefaultCacheItemForViewModel(vm);
        }

        public static Type GetViewTypeForModel(IViewModel rootModel)
        {
            return UIManager.GetViewType(rootModel);
        }



        public static FrameworkElement CreateOrGetDefaultView(IViewModel vm/*, bool forceCreate=false*/)
        {
            var iview = DefaultViewCache.GetDefaultView(vm);
            if (iview != null) return iview;
            var type = GetViewTypeForModel(vm);
            if (type != null)
            {
                iview = (FrameworkElement)Activator.CreateInstance(type);
                UIManager.Bind(vm, iview, null);
                
                DefaultViewCache.EnsureInCache(vm, iview);
                return iview;
            }
            return null;
        }



        public static void DisposeViews(bool disposing)
        {
            if (disposing)
            {
                if (DefaultViewCache != null)
                {
                    DefaultViewCache.Dispose();
                }
            }
        }
    }
}
